/*
 * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "device_resource_if.h"
#include "hdf_base.h"
#include "hdf_log.h"
#include "osal_mem.h"
#include "uart_core.h"
#include "stm32l4xx_hal.h"

#define STM32L4XX_UART_TIMEOUT 0xFFFF // ticks

#ifdef HAL_UART_MODULE_ENABLED
USART_TypeDef *Stm32l4xxGetUartInstance(int16_t number)
{
    if (number < 1 || number > 5) {
        return NULL;
    }
    switch (number) {
        case 1:
            return USART1;
        case 2:
            return USART2;
        case 3:
            return USART3;
        case 4:
            return UART4;
        case 5:
            return UART5;
        default:
            break;
    }
    return NULL;
}

static int32_t Stm32l4xxReadConfigFromHcs(struct UartHost *host)
{
    const struct DeviceResourceNode *node = NULL;
    struct DeviceResourceIface *iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);

    if (host == NULL || host->device == NULL) {
        HDF_LOGE("%s: host or device null", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    node = host->device->property;
    if (node == NULL) {
        HDF_LOGE("%s: properity node null", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    if (iface == NULL || iface->GetUint32 == NULL) {
        HDF_LOGE("%s: face is invalid", __func__);
        return HDF_ERR_NOT_SUPPORT;
    }

    if (iface->GetUint32(node, "num", &host->num, 0) != HDF_SUCCESS) {
        HDF_LOGE("%s: read busNum fail", __func__);
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

static int32_t Stm32l4xxUartInit(struct UartHost *host)
{
    int32_t ret;

    UART_HandleTypeDef *huart = NULL;

    HDF_LOGI("%s: enter", __func__);
    if (host == NULL || host->device == NULL) {
        return HDF_ERR_INVALID_OBJECT;
    }

    huart = (UART_HandleTypeDef *)OsalMemCalloc(sizeof(*huart));
    if (huart == NULL) {
        return HDF_ERR_MALLOC_FAIL;
    }

    ret = Stm32l4xxReadConfigFromHcs(host);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("Stm32l4xxUartInit: read hcs config failed");
        OsalMemFree(huart);
        return ret;
    }
    // default value
    huart->Instance = Stm32l4xxGetUartInstance(host->num);
    //huart->Instance = NULL;
    if (huart->Instance == NULL) {
        HDF_LOGE("%s: get inst of num:%u failed!", __func__, host->num);
        return HDF_FAILURE;
    }
    huart->Init.BaudRate = 115200;
    huart->Init.WordLength = UART_WORDLENGTH_8B;
    huart->Init.StopBits = UART_STOPBITS_1;
    huart->Init.Parity = UART_PARITY_NONE;
    huart->Init.Mode = UART_MODE_TX_RX;
    huart->Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart->Init.OverSampling = UART_OVERSAMPLING_16;
    huart->Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
    huart->Init.ClockPrescaler = UART_PRESCALER_DIV1;
    huart->AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
    if (HAL_UART_Init(huart) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_UARTEx_SetTxFifoThreshold(huart, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_UARTEx_SetRxFifoThreshold(huart, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_UARTEx_DisableFifoMode(huart) != HAL_OK)
    {
        Error_Handler();
    }
    host->priv = huart;
    return HDF_SUCCESS;
}

static int32_t Stm32l4xxUartDeinit(struct UartHost *host)
{
    UART_HandleTypeDef *huart = NULL;
    HDF_LOGI("%s: enter", __func__);

    if (host == NULL || host->priv == NULL) {
        return HDF_ERR_INVALID_OBJECT;
    }
    huart = (UART_HandleTypeDef *)host->priv;
    
    (void)HAL_UART_DeInit(huart);
    return HDF_SUCCESS;
}

static int32_t Stm32l4xxUartRead(struct UartHost *host, uint8_t *data, uint32_t size)
{
    UART_HandleTypeDef *huart = NULL;
    HAL_StatusTypeDef ret;

    if (host == NULL || host->priv == NULL) {
        HDF_LOGE("%s: host or priv null", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    huart = (UART_HandleTypeDef *)host->priv;
    ret = HAL_UART_Receive(huart, data, size, STM32L4XX_UART_TIMEOUT);
    if (ret != HAL_OK) {
        HDF_LOGE("%s: uart hal receive fail:%d", __func__, ret);
        return HDF_FAILURE;
    }
    return size;
}

static int32_t Stm32l4xxUartWrite(struct UartHost *host, uint8_t *data, uint32_t size)
{
    UART_HandleTypeDef *huart = NULL;
    HAL_StatusTypeDef ret;

    if (host == NULL || host->priv == NULL) {
        HDF_LOGE("%s: host or priv null", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    huart = (UART_HandleTypeDef *)host->priv;
    ret = HAL_UART_Transmit(huart, data, size, STM32L4XX_UART_TIMEOUT);
    if (ret != HAL_OK) {
        HDF_LOGE("%s: uart hal transmit fail:%d", __func__, ret);
        return HDF_FAILURE;
    }
    return size;
}

static int32_t Stm32l4xxUartSetBaud(struct UartHost *host, uint32_t baudRate)
{
    return HDF_SUCCESS;
}

static int32_t Stm32l4xxUartGetBaud(struct UartHost *host, uint32_t *baudRate)
{
    UART_HandleTypeDef *huart = NULL;
    HAL_StatusTypeDef ret;

    if (host == NULL || host->priv == NULL) {
        HDF_LOGE("%s: host or priv null", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    huart = (UART_HandleTypeDef *)host->priv;
    *baudRate = huart->Init.BaudRate;
    return HDF_SUCCESS;
}

static int32_t Stm32l4xxUartSetAttribute(struct UartHost *host, struct UartAttribute *attribute)
{
    HDF_LOGE("%s: not supported yet", __func__);
    return HDF_ERR_NOT_SUPPORT;
}

static int32_t Stm32l4xxUartGetAttribute(struct UartHost *host, struct UartAttribute *attribute)
{
    HDF_LOGE("%s: not supported yet", __func__);
    return HDF_ERR_NOT_SUPPORT;
}

static int32_t Stm32l4xxUartSetTransMode(struct UartHost *host, enum UartTransMode mode)
{
    HDF_LOGE("%s: not supported yet", __func__);
    return HDF_ERR_NOT_SUPPORT;
}

struct UartHostMethod g_uartHostMethod = {
    .Read = Stm32l4xxUartRead,
    .Write = Stm32l4xxUartWrite,
    .SetBaud = Stm32l4xxUartSetBaud,
    .GetBaud = Stm32l4xxUartGetBaud,
    .SetAttribute = Stm32l4xxUartSetAttribute,
    .GetAttribute = Stm32l4xxUartGetAttribute,
    .SetTransMode = Stm32l4xxUartSetTransMode,
};

static int32_t HdfStm32l4xxBind(struct HdfDeviceObject *device)
{
    HDF_LOGI("%s: entry", __func__);
    if (device == NULL) {
        return HDF_ERR_INVALID_OBJECT;
    }
    if (UartHostCreate(device) == NULL) {
        return HDF_FAILURE;
    }
    HDF_LOGI("%s: bind success!", __func__);
    return HDF_SUCCESS;
}

int32_t HdfStm32l4xxInit(struct HdfDeviceObject *device)
{
    int32_t ret;
    struct UartHost *host = NULL;

    HDF_LOGI("%s: entry", __func__);
    if (device == NULL) {
        HDF_LOGE("%s: device is null", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    host = UartHostFromDevice(device);
    if (host == NULL) {
        HDF_LOGE("%s: host is null", __func__);
        return HDF_FAILURE;
    }
    ret = Stm32l4xxUartInit(host);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: uart init error: %d", __func__, ret);
        return HDF_FAILURE;
    }
    host->method = &g_uartHostMethod;
    HDF_LOGI("%s: uart-%u init success!", __func__, host->num);
    return HDF_SUCCESS;
}

void HdfStm32l4xxRelease(struct HdfDeviceObject *device)
{
    struct UartHost *host = NULL;

    HDF_LOGI("%s: entry", __func__);
    if (device == NULL) {
        HDF_LOGE("%s: device is null", __func__);
        return;
    }
    host = UartHostFromDevice(device);
    if (host == NULL) {
        HDF_LOGE("%s: host is null", __func__);
        return;
    }
    (void)Stm32l4xxUartDeinit(host);
    UartHostDestroy(host);
}

struct HdfDriverEntry g_hdfUartDevice = {
    .moduleVersion = 1,
    .moduleName = "stm32l4xx_uart_driver",
    .Bind = HdfStm32l4xxBind,
    .Init = HdfStm32l4xxInit,
    .Release = HdfStm32l4xxRelease,
};

HDF_INIT(g_hdfUartDevice);
#endif /* HAL_UART_MODULE_ENABLED */
